package com.luxsoft.siipap.em.replica.importadores.ventas;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.util.Assert;

import com.luxsoft.siipap.cxc.model.OrigenDeOperacion;
import com.luxsoft.siipap.em.replica.importadores.ImportacionCanceledException;
import com.luxsoft.siipap.em.replica.importadores.ImportadorSupport;
import com.luxsoft.siipap.em.replica.importadores.cxc.ImportadorDeClientes;
import com.luxsoft.siipap.model.Periodo;
import com.luxsoft.siipap.model.Sucursal;
import com.luxsoft.siipap.model.core.Cliente;
import com.luxsoft.siipap.service.ServiceLocator2;
import com.luxsoft.siipap.util.DBUtils;
import com.luxsoft.siipap.util.DateUtil;
import com.luxsoft.siipap.util.ValidationUtils;
import com.luxsoft.siipap.ventas.dao.VentaDao;
import com.luxsoft.siipap.ventas.model.Cobrador;
import com.luxsoft.siipap.ventas.model.Vendedor;
import com.luxsoft.siipap.ventas.model.Venta;
import com.luxsoft.siipap.ventas.model.VentaCredito;
import com.luxsoft.siipap.ventas.model.VentaDet;

/**
 * Importador de Ventas desde Oracle
 * 
 * @author Ruben Cancino
 *
 */
public class ImportadorDeVentas {
	
	
	private Logger logger=Logger.getLogger(getClass());
	
	private ImportadorDeVentasDet importadorDePartidas;
	
	private ImportadorSupport support=ImportadorSupport.getInstance();
	
	public ImportadorDeVentas() {
		importadorDePartidas=new ImportadorDeVentasDet(support);
		importadorDePartidas.setJdbcTemplate(getSourceJdbcTemplate());
	}
	
	public int contar(Periodo p){
		String sql="select count(venta_id) from v_ventas " +
		"where fecha between ? and ?";
		SqlParameterValue p1=new SqlParameterValue(Types.DATE,p.getFechaInicial());
		SqlParameterValue p2=new SqlParameterValue(Types.DATE,p.getFechaFinal());
		int res=getSourceJdbcTemplate().queryForInt(sql,new Object[]{p1,p2} );
		return res;
	}
	
	public void importar(Periodo periodo,int segmentos){
		setTotal(contar(periodo));
		List<Periodo> periodos=DateUtil.segmentar(periodo, segmentos);
		for(Periodo p:periodos){
			System.out.print(MessageFormat.format("\nImportando: {0}", p));
			importarPorPeriodo(p);
		}
		
	}
	
	private void importarPorPeriodo(final Periodo p){
		
		System.out.print(" Mapeando ");
		String sql="select b.VENCIMIENTO,a.DESCUENTO as DESCUENTO,b.CARGO as BCARGO,b.COMENTARIO as BCOMENTARIO,b.COMENTARIOREPPAGO as BCOMENTARIO2,b.DIAPAGO as BDIAPAGO,b.FECHARECEPCIONCXC ,b.FECHAREVISION,b.FECHAREVISIONCXC,b.PLAZO,b.REPROGRAMARPAGO,b.REVISADA,b.REVISION ,a.* from v_ventas a left join SW_VENTASCREDITO b on(a.venta_id=b.venta_id) where a.fecha between ? and ? order by a.venta_id";
		
		SqlParameterValue p1=new SqlParameterValue(Types.DATE,p.getFechaInicial());
		SqlParameterValue p2=new SqlParameterValue(Types.DATE,p.getFechaFinal());
		Object[] params=new Object[]{p1,p2};
		List<Venta> ventas=getSourceJdbcTemplate().query(sql,params,new FacturaMapper());
		
		System.out.print(" Ventas: "+ventas.size());		
		
		
		String sql2="SELECT * FROM V_VENTASDET WHERE FECHA BETWEEN ? AND ? order by VENTA_ID";
		List<VentaDet> partidas=getSourceJdbcTemplate().query(sql2, params, importadorDePartidas);
		System.out.print("  Partidas:"+partidas.size());
		
		
		for(final Venta target:ventas){
			try {
				Collection<VentaDet> parts=CollectionUtils.select(partidas, new Predicate(){
					public boolean evaluate(Object input) {
						VentaDet det=(VentaDet)input;
						return det.siipapWinVentaId.equals(target.getSiipapWinId());
					}
				});
				
				for(VentaDet dd:parts)
					target.agregarPartida(dd);
				
				importarVenta(target);
				updateCurrent();
				System.out.print(".");
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println(e.getMessage());
				logger.error("Imposible persistir la venta: "+target.getSiipapWinId());
			}
		}
		
	}
	
	/**
	 * Trata de importar a MySQL una venta ya mapeada
	 * 
	 * @param target
	 * @param confirmarExistencia Permite confirmar si esta ya existe para solo actualizarla
	 * @return
	 */
	private Venta importarVenta(final Venta target) throws Exception{
		Venta source=getVentaDao().buscarPorOracleId(target.getSiipapWinId());
		if(source!=null){
			//La venta ya exsite,  copiar sus propiedades
			BeanUtils.copyProperties(target, source,new String[]{"id","version","cliente","credito"});
			VentaCredito credito=target.getCredito();
			if(credito!=null){
				credito.setVenta(source);
				source.setCredito(credito);
			}
			//source.getPartidas().clear();
			for(VentaDet det:target.getPartidas()){
				if(!target.getPartidas().contains(det))
					source.agregarPartida(det);
			}
			return persistir(source);
		}else{
			Cliente c=support.buscarCliente(target.getClave());
			//Cliente c=buscarCliente(target.getClave());
			if(c==null){
				c=ServiceLocator2.getClienteManager().save(new Cliente(target.getClave(),target.getNombre()));
				//clientes.add(c);
			}
			String nombre=target.getNombre();
			target.setCliente(c);
			target.setNombre(nombre);
			target.setComentario2("Venta importada el: "+df.format(new Date()));
			return persistir(target);
		}
		
	}
	
	/**
	 * Genera una Venta en el nuevo sistema a partir de un registro del anterior 
	 * Si esta existe solo la actualiza
	 * 
	 * @param ventaId
	 * @return
	 */
	public Venta importar(long ventaId){
		Venta target=getVentaDao().buscarPorOracleId(ventaId);
		if(target==null){
			target=map(ventaId);
			if(target==null){
				logger.info("No existe la venta: "+ventaId);
				return null;
			}
			Cliente c=ServiceLocator2.getClienteManager().buscarPorClave(target.getClave());
			if(c==null){
				String clave=target.getClave();
				String nombre=target.getNombre();
				c=new Cliente(clave,nombre);
			}
			String nombre=target.getNombre();
			target.setCliente(c);
			target.setNombre(nombre);
			
			target.setComentario2("Venta importada el: "+df.format(new Date()));
			target=persistir(target);
			logger.info("Factura nueva importada: "+target);
			updateCurrent();
		}else{
			Venta source=map(ventaId);
			BeanUtils.copyProperties(source, target,new String[]{"id","version","cliente","credito"});
			if(source.getCredito()!=null){				
				target.getPartidas().clear();
				for(VentaDet det:source.getPartidas()){
					target.agregarPartida(det);
				}
			}
			target=persistir(target);
			logger.info("CXPFactura  ya importado propiedades copiadas en la factura:"+target);
			updateCurrent();
		}
		
		return target;
	}
	
	/**
	 * Importa la cartera de credito
	 * 
	 */
	public void importarCarteraDeCredito(){
		logger.info("Calculando el total de cuentas por cobrar a importar");
		setTotal(getSourceJdbcTemplate().queryForInt("select count(venta_id) from V_VENTAS where year>2007 and saldo>0 and origen=\'CRE\'"));
		String sql="select venta_id from V_VENTAS where year>2007 and saldo>0 and origen=\'CRE\' ";
		List<Long> res=getSourceJdbcTemplate().queryForList(sql,Long.class);
		for(Long id:res){
			try {
				importar(id);
			} catch (ImportacionCanceledException ce) {
				throw ce;
			}catch (Exception e) {
				e.printStackTrace();
				logger.error("No se pudo importar la venta:"+id);
			}
		}
	}
	
	DateFormat df=new SimpleDateFormat("dd/MM/yyyy:hh:mm");
	
	protected Venta persistir(Venta fac){
		if(fac.getDiaPago()==null)
			fac.setDiaPago(new Date());
		if(fac.getFechaRevision()==null)
			fac.setFechaRevision(new Date());
		if(fac.getFechaRevisionCxc()==null)
			fac.setFechaRevisionCxc(new Date());
		if(fac.getReprogramarPago()==null)
			fac.setReprogramarPago(new Date());
		return getVentaDao().save(fac);
	}
	
	private Venta map(final long ventaid){
		String sql="select b.VENCIMIENTO,a.DESCUENTO as DESCUENTO,b.CARGO as BCARGO,b.COMENTARIO as BCOMENTARIO,b.COMENTARIOREPPAGO as BCOMENTARIO2,b.DIAPAGO as BDIAPAGO,b.FECHARECEPCIONCXC ,b.FECHAREVISION,b.FECHAREVISIONCXC,b.PLAZO,b.REPROGRAMARPAGO,b.REVISADA,b.REVISION ,a.* from v_ventas a left join SW_VENTASCREDITO b on(a.venta_id=b.venta_id) where a.venta_id=?";
		List<Venta> res=getSourceJdbcTemplate().query(sql,new Object[]{ventaid}, new FacturaMapper());
		Assert.notEmpty(res,"No existe en oracle la venta_id: "+ventaid);
		Venta fac= res.isEmpty()?null:res.get(0);
		fac.setSiipapWinId(ventaid);
		this.importadorDePartidas.importar(fac);
		return fac;
	}
	
	public VentaDao getVentaDao(){
		return (VentaDao)ServiceLocator2.instance().getContext().getBean("ventaDao");
	}

	public JdbcTemplate getSourceJdbcTemplate() {
		return ServiceLocator2.getAnalisisJdbcTemplate();
	}
	
	private int current;
	
	private int total;
	
	public void setProgress(int progress){
		
	}
	
	protected void updateCurrent(){
		current++;		
		if(total!=0){
			int progress=current*100/this.total;			
			setProgress(progress);
		}
	}
	
	public void setTotal(int total) {
		this.total = total;
		logger.info("Total de ventas a importar:"+total);
	}

	public  class FacturaMapper implements RowMapper{

		public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
			Venta fac=new Venta();
			fac.setSiipapWinId(rs.getLong("VENTA_ID"));
			fac.setClave(rs.getString("CLAVE"));
			fac.setNombre(rs.getString("NOMBRE"));
			fac.setComentario(rs.getString("COMENTARIO"));
			String serie=rs.getString("SERIE");
			if(serie.equals("E")){
				//Procesar credito
				VentaCredito credito=new VentaCredito();
				//fac.setCargo(rs.getDouble("BCARGO"));
				fac.setComentario(rs.getString("BCOMENTARIO"));
				fac.setComentarioRepPago(rs.getString("BCOMENTARIO2"));
				fac.setDiaPago(rs.getDate("BDIAPAGO"));
				fac.setFechaRecepcionCXC(rs.getDate("FECHARECEPCIONCXC"));
				fac.setFechaRevision(rs.getDate("FECHAREVISION"));
				fac.setFechaRevisionCxc(rs.getDate("FECHAREVISIONCXC"));
				fac.setNumeroFiscal(rs.getInt("NUMEROFISCAL"));
				fac.setPlazo(rs.getInt("PLAZO"));
				fac.setReprogramarPago(rs.getDate("REPROGRAMARPAGO"));
				fac.setRevisada(rs.getBoolean("REVISADA"));
				fac.setRevision(rs.getBoolean("REVISION"));
				credito.setVenta(fac);
				fac.setCredito(credito);
				boolean val=ValidationUtils.isValid(credito);
				if(!val){
					ValidationUtils.debugValidation(credito);
					Assert.isTrue(val,"No es correcta la informacion para credito");
				}				
			}
			
			fac.setDocumento(rs.getLong("NUMERO"));
			fac.setFecha(rs.getDate("FECHA"));
			
			Date vencimiento=rs.getDate("VENCIMIENTO");
			fac.setVencimiento(vencimiento!=null?vencimiento:fac.getFecha());
			
			fac.setImporte(rs.getBigDecimal("SUBTOTAL"));
			fac.setImpuesto(rs.getBigDecimal("IMPUESTO"));
			String sorigen=rs.getString("ORIGEN");
			if("CRE".equals(sorigen))
				fac.setOrigen(OrigenDeOperacion.CRE);
			else if("MOS".equals(sorigen))
				fac.setOrigen(OrigenDeOperacion.MOS);
			else if("CAM".equals(sorigen))
				fac.setOrigen(OrigenDeOperacion.CAM);
			else if("JUR".equals(sorigen))
				fac.setOrigen(OrigenDeOperacion.JUR);
			else if("CHE".equals(sorigen))
				fac.setOrigen(OrigenDeOperacion.CHE);
			fac.setSaldo(rs.getBigDecimal("SALDO"));
			fac.setSerieSiipap(serie);
			
			Sucursal suc=support.buscarSucursal(rs.getInt("SUCURSAL"));
			fac.setSucursal(suc);
			fac.setSurtidor(rs.getString("SURTIDOR"));
			fac.setTipoSiipap(rs.getString("TIPO"));
			
			BigDecimal descuento=rs.getBigDecimal("DESCUENTO");
			fac.setDescuentoGeneral(descuento.abs().doubleValue());
			
			if(fac.getTipoSiipap().equals("G"))
				fac.setPostFechado(true);
			
			if(fac.getTipoSiipap().equals("N")
					|| fac.getTipoSiipap().equals("P")
					||fac.getTipoSiipap().equals("X")
					||fac.getTipoSiipap().equals("G")){
				fac.setPrecioBruto(false);
				fac.setDescuentoGeneral(0);
			}
			fac.setTotal(rs.getBigDecimal("TOTAL"));
			
			BigDecimal importeCortes=rs.getBigDecimal("IMPCORTES");
			if(importeCortes==null) importeCortes=BigDecimal.ZERO;
			BigDecimal importeMo=rs.getBigDecimal("IMPMANIOBRAS");
			if(importeMo==null) importeCortes=BigDecimal.ZERO;
			
			fac.setCargos(importeCortes.add(importeMo));
			if((fac.getCargos()!=null) && (fac.getCargos().doubleValue()>0)){
				if(fac.getCargos().doubleValue()>0){
					fac.setDescCargo("CORTES-MO");
				}else
					fac.setDescCargo(null);
			}
			
			fac.setBonificaciones(rs.getBigDecimal("BONIFICACIONES"));
			fac.setDescuentos(rs.getBigDecimal("DESCUENTOS"));
			fac.setDevoluciones(rs.getBigDecimal("DEVOLUCIONES"));
			
			
			fac.setDiaDelPago(rs.getInt("DIAPAGO"));
			fac.setDiaRevision(rs.getInt("DIAREVISION"));
			fac.setCobrador(buscarCobrador(rs.getInt("COBRADOR")));
			fac.setVendedor(buscarVendedor(rs.getInt("VENDEDOR")));
			return fac;
		}
	}
	
	private List<Cobrador> cobradores;
	private List<Vendedor> vendedores;
	
	public Cobrador buscarCobrador(final int id ){
		if(cobradores==null){
			cobradores=ServiceLocator2.getUniversalDao().getAll(Cobrador.class);
		}
		return (Cobrador)CollectionUtils.find(cobradores, new Predicate(){
			public boolean evaluate(Object input) {
				Cobrador target=(Cobrador)input;
				return target.getId().intValue()==id;
			}
		});
	}
	
	public Vendedor buscarVendedor(final int id ){
		if(vendedores==null){
			vendedores=ServiceLocator2.getUniversalDao().getAll(Vendedor.class);
		}
		return (Vendedor)CollectionUtils.find(vendedores, new Predicate(){
			public boolean evaluate(Object input) {
				Vendedor target=(Vendedor)input;
				return target.getId().intValue()==id;
			}
		});
	}
	
	private static ImportadorDeVentas INSTANCE;
	
	public static ImportadorDeVentas instance(){
		if(INSTANCE==null){
			INSTANCE=new ImportadorDeVentas();
		}
		return INSTANCE;
	}
	
	private ImportadorDeClientes importadorDeClientes;
	
	
	public ImportadorDeClientes getImportadorDeClientes() {
		if(importadorDeClientes==null)
			importadorDeClientes=new ImportadorDeClientes();
		return importadorDeClientes;
	}

	public void setImportadorDeClientes(ImportadorDeClientes importadorDeClientes) {
		this.importadorDeClientes = importadorDeClientes;
	}

	public static void main(String[] args) {
		DBUtils.whereWeAre();
		
		ImportadorDeVentas imp=new ImportadorDeVentas();
		//imp.importar(new Periodo("05/08/2009","05/08/2009"),10);
		Long[] ids={3271797L};
		for(Long id:ids){
			imp.importar(id);
		}
		
	}

}